/**
 * 用于包装一个函数并返回一个新函数，使其调用这个新函数只有一个promise在执行，后续的调用都会返回这个promise
 * @param fn {function(...[*]): (Promise<Awaited<*>>)} - 需要包装的函数
 * @param opt {object} - 配置项
 * @param opt.cache {number} [opt.cache = 0] - 缓存时间，单位毫秒，如果大于0，则会缓存结果，下次调用如何结果仍在有效缓存时间内，则会直接返回缓存的结果。默认0，不缓存
 * @return {(function(...[*]): (Promise<Awaited<*>>))|*}
 */

function singlePromise(fn, opt = {}) {
  const defaultOpt = {
    cache: 0,
  };
  let promise;
  let cacheMill = -1;
  let cacheRes;

  opt = Object.assign({}, defaultOpt, opt);

  function handle(...args) {
    let self = this;
    let { cache } = opt;

    if (cache && cacheMill > 0) {
      if (Date.now() - cacheMill < cache) {
        return Promise.resolve(cacheRes);
      } else {
        cacheMill = -1;
        cacheRes = null;
      }
    }
    if (promise) {
      return promise;
    } else {
      promise = Promise.resolve(fn.apply(self, args));
    }
    return promise
      .then((res) => {
        if (cache) {
          cacheMill = Date.now();
          cacheRes = res;
        }
        promise = null;
        return res;
      })
      .catch((err) => {
        promise = null;
        return Promise.reject(err);
      });
  }

  handle.clear = function () {
    cacheMill = -1;
    cacheRes = null;
  };
  handle.update = function (newOpt = {}) {
    opt = Object.assign({}, opt, newOpt);
  };

  return handle;
}

export { singlePromise };

// test
{
  const getTime = () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(Date.now());
      }, 1000);
    });
  };

  const singleGetTime = singlePromise(getTime);

  for (let i = 0; i < 5; i++) {
    singleGetTime().then((res) => {
      console.log('res', res);
    });
  }
  // res 1694058404950
  // res 1694058404950
  // res 1694058404950
  // res 1694058404950
  // res 1694058404950
  // 输出5遍结果,但只获取了一次当前时间
}

import axios from 'axios';
import Cookies from 'js-cookie';
{
  // access_token 无痛刷新
  const http = axios.create({});

  const getAccessToken = (refreshToken) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('新的access token：' + Date.now());
      }, 1500);
    });
  };

  const singleGetAccessToken = singlePromise(getAccessToken);

  http.interceptors.request.use(async (config) => {
    const token = Cookies.get('access_token');
    if (!token) {
      const refreshToken = Cookies.get('refresh_token');
      const res = await singleGetAccessToken(refreshToken);
      Cookies.set('access_token', res);
    }
    return config;
  });
}

{
  // 多个组件同时获取用户信息，使用singlePromise 包装，只需要调用一次。
  // api.js
  // 多次调用，在api 请求未响应前，只会调用一次api请求，请求响应后会把api 响应结果响应给对应的调用。调用结束后 1000 ms 内，再次调用会直接返回上次调用的结果，不会再次调用api 请求。调用结束后 1000 ms 后，再次调用会直接调用api请求
  const getUserInfo = singlePromise(
    (params) => axios.get('api/userInfo', { params }),
    { cache: 1000 }
  );
}
